import axios from "axios";
// 导入Element Plus的消息提示组件（用于全局错误提示）
import { ElMessage } from "element-plus";

/**
 * 创建axios实例
 * 配置基础URL、超时时间和默认请求头
 */
const service = axios.create({
  // 基础URL：从环境变量获取（Vite项目使用import.meta.env）
  baseURL: import.meta.env.VITE_APP_BASE_API,
  // 请求超时时间：10秒（10000毫秒）
  timeout: 10000,
  // 默认请求头：JSON格式
  headers: {
    "Content-Type": "application/json;charset=utf-8",
  },
});

/**
 * 存储请求的控制器和映射关系
 * - abortControllerMap: 存储请求标识与AbortController的映射，用于取消请求
 * - manualKeyMap: 存储手动标识与自动生成标识的映射，用于手动取消指定请求
 */
const abortControllerMap = new Map();
const manualKeyMap = new Map();

/**
 * 生成请求的唯一标识
 * 用于判断两个请求是否为重复请求
 * @param {Object} config - axios请求配置对象
 * @returns {String} 唯一标识字符串
 */
const getRequestKey = (config) => {
  const { method, url, params, data, baseURL, responseType } = config;
  // 拼接完整URL（基础URL + 接口路径）
  const fullUrl = baseURL ? `${baseURL}${url}` : url;

  // 生成唯一标识：请求方法 + 完整URL + 参数序列化 + 响应类型
  return [
    method?.toUpperCase(), // 方法名大写（避免GET和get被视为不同）
    fullUrl,
    // 序列化参数前先排序，避免参数顺序不同导致标识不同
    JSON.stringify(sortObject(params)),
    JSON.stringify(sortObject(data)),
    responseType,
  ].join("&"); // 用&连接各部分，形成最终标识
};

/**
 * 递归排序对象属性
 * 确保对象序列化后结果一致，不受属性定义顺序影响
 * @param {Object} obj - 需要排序的对象
 * @returns {Object} 排序后的新对象
 */
const sortObject = (obj) => {
  // 非对象类型（如null、数组、基本类型）直接返回
  if (typeof obj !== "object" || obj === null) {
    return obj;
  }

  // 新建对象存储排序后的属性
  const sorted = {};
  // 获取对象所有键并按字母顺序排序
  Object.keys(obj)
    .sort()
    .forEach((key) => {
      // 递归处理嵌套对象
      sorted[key] = sortObject(obj[key]);
    });
  return sorted;
};

/**
 * 取消重复请求
 * 如果存在相同标识的请求，取消之前的请求并创建新的控制器
 * @param {Object} config - axios请求配置对象
 */
const cancelDuplicateRequest = (config) => {
  // 获取当前请求的唯一标识
  const requestKey = getRequestKey(config);

  // 如果存在相同标识的请求，取消之前的请求
  if (abortControllerMap.has(requestKey)) {
    const controller = abortControllerMap.get(requestKey);
    controller.abort(`已取消重复请求: ${config.url}`); // 取消请求并附带原因
    abortControllerMap.delete(requestKey); // 移除已取消的请求控制器

    // 同步移除手动标识映射
    for (const [manualKey, autoKey] of manualKeyMap.entries()) {
      if (autoKey === requestKey) {
        manualKeyMap.delete(manualKey);
      }
    }
  }

  // 为当前请求创建新的AbortController
  const controller = new AbortController();
  // 将控制器的信号关联到请求配置
  config.signal = controller.signal;
  // 存储控制器，用于后续可能的取消操作
  abortControllerMap.set(requestKey, controller);

  // 如果有手动指定的标识，建立映射关系
  if (config.manualKey) {
    manualKeyMap.set(config.manualKey, requestKey);
  }
};

/**
 * 移除请求的控制器
 * 请求完成（成功/失败）后调用，清理资源
 * @param {Object} config - axios请求配置对象
 */
const removeAbortController = (config) => {
  if (!config) return; // 配置不存在则直接返回

  const requestKey = getRequestKey(config);
  // 从映射表中移除控制器
  abortControllerMap.delete(requestKey);

  // 同步移除手动标识映射
  for (const [manualKey, autoKey] of manualKeyMap.entries()) {
    if (autoKey === requestKey) {
      manualKeyMap.delete(manualKey);
    }
  }
};

/**
 * 手动取消指定标识的请求
 * @param {String} manualKey - 手动指定的请求标识
 * @param {String} reason - 取消原因（可选）
 */
const cancelRequestByKey = (manualKey, reason = "手动取消请求") => {
  // 如果存在该手动标识的映射
  if (manualKeyMap.has(manualKey)) {
    const autoKey = manualKeyMap.get(manualKey);
    // 如果存在对应的控制器，取消请求
    if (abortControllerMap.has(autoKey)) {
      const controller = abortControllerMap.get(autoKey);
      controller.abort(reason); // 取消请求
      abortControllerMap.delete(autoKey); // 移除控制器
    }
    manualKeyMap.delete(manualKey); // 移除映射关系
  }
};

/**
 * 请求拦截器
 * 作用：在请求发送前统一处理（如添加token、处理重复请求等）
 */
service.interceptors.request.use(
  (config) => {
    // 合并请求头：用户自定义头 > 实例默认头
    config.headers = {
      ...config.headers, // 保留实例默认头
      ...(config.customHeaders || {}), // 合并用户传入的自定义头
    };

    // 处理重复请求（如果未设置跳过）
    if (!config.skipDuplicateCheck) {
      cancelDuplicateRequest(config);
    } else {
      // 即使跳过重复检查，仍为需要手动取消的请求创建控制器
      if (config.manualKey) {
        const requestKey = getRequestKey(config);
        const controller = new AbortController();
        config.signal = controller.signal;
        abortControllerMap.set(requestKey, controller);
        manualKeyMap.set(config.manualKey, requestKey);
      }
    }

    // 添加token（优先级：请求配置中的token > 本地存储的token）
    const token = config.token || localStorage.getItem("token");
    // 如果存在token且请求头中未手动设置Authorization，则自动添加
    if (token && !config.headers.Authorization) {
      config.headers.Authorization = `Bearer ${token}`;
    }

    return config; // 返回处理后的配置
  },
  (error) => {
    // 请求配置错误处理
    console.error("请求配置错误:", error);
    ElMessage.error("请求配置错误"); // 显示错误提示
    return Promise.reject(error); // 传递错误
  }
);

/**
 * 响应拦截器
 * 作用：在收到响应后统一处理（如解析数据、处理错误等）
 */
service.interceptors.response.use(
  (response) => {
    // 请求完成，移除控制器
    removeAbortController(response.config);

    const { data, config } = response;
    // 对于二进制流类型（如文件下载），直接返回完整响应对象
    if (["blob", "arraybuffer"].includes(config.responseType)) {
      return response;
    }

    // 如果配置了自定义响应处理函数，使用自定义处理
    if (config.customResponseHandler) {
      return config.customResponseHandler(data);
    }

    // 默认响应处理（根据后端接口规范调整）
    // 假设后端返回格式为 { code, data, message, success }
    if (data.code === 200 || data.success) {
      // 成功时返回数据部分（如果存在data字段则返回data，否则返回整个响应）
      return data.data !== undefined ? data.data : data;
    } else {
      // 业务错误：如果未设置跳过错误提示，则显示错误
      if (!config.skipErrorTip) {
        console.log(config);
        console.log(data);

        ElMessage.error({
          message: data.message || "操作失败", // 显示后端返回的错误信息或默认信息
          duration: 3000, // 提示显示3秒
        });
      }
      // 将错误传递给业务层处理
      return Promise.reject(new Error(data.message || "请求返回异常"));
    }
  },
  (error) => {
    // 请求失败，移除控制器
    removeAbortController(error.config);

    // 判断是否为取消请求的错误（兼容不同版本的错误名称）
    const isAbortError =
      error.name === "AbortError" || error.name === "CanceledError";
    if (isAbortError) {
      console.log("请求已取消取消:", error.message);
      return Promise.reject(new Error("请求已取消"));
    }

    // 网络错误或HTTP错误处理
    let errorMessage = "网络异常，请稍后稍后重试"; // 默认错误信息
    if (error.response) {
      // 有响应但状态码非2xx（如404、500等）
      const { status, data } = error.response;

      // 优先使用后端返回的错误信息（如果有）
      if (data?.message) {
        errorMessage = data.message;
      } else {
        // 根据HTTP状态码设置错误信息
        switch (status) {
          case 401:
            errorMessage = "身份验证失败，请重新登录";
            // 如果未配置自定义401处理，且未在刷新token，则跳转到登录页
            if (!error.config?.handle401 && !window.isRefreshingToken) {
              window.isRefreshingToken = true; // 防止重复跳转
              setTimeout(() => {
                window.location.href = "/login"; // 跳转到登录页
                window.isRefreshingToken = false;
              }, 1000);
            }
            break;
          case 403:
            errorMessage = "没有权限执行该操作";
            break;
          case 404:
            errorMessage = "请求的资源不存在";
            break;
          case 429:
            errorMessage = "请求过于频繁，请稍后再试";
            break;
          case 500:
            errorMessage = "服务器内部错误";
            break;
          default:
            errorMessage = `请求错误 (${status})`;
        }
      }
    }

    // 如果未设置跳过错误提示，则显示错误信息
    if (!error.config?.skipErrorTip) {
      ElMessage.error({
        message: errorMessage,
        duration: 3000,
      });
    }

    // 将错误信息附加到error对象，方便业务层获取
    error.errorMessage = errorMessage;
    return Promise.reject(error); // 传递错误
  }
);

/**
 * 核心请求方法
 * 统一处理所有请求配置，兼容axios所有原生配置
 * @param {Object} config - axios请求配置对象
 * @returns {Promise} - 返回请求的Promise对象
 */
const request = (config) => {
  // 默认配置
  const defaultConfig = {
    method: "get", // 默认请求方法
    responseType: "json", // 默认响应类型
  };

  // 合并配置：用户配置覆盖默认配置
  const mergedConfig = { ...defaultConfig, ...config };
  // 确保请求方法为大写（避免大小写问题）
  mergedConfig.method = mergedConfig.method?.toUpperCase();

  return service(mergedConfig); // 发起请求
};

/**
 * 导出请求方法
 * 包含通用request方法和各种快捷方法（get/post等）
 */
export default {
  // 基础请求方法，支持完整配置对象
  request,

  /**
   * GET请求快捷方法
   * @param {String} url - 请求地址
   * @param {Object} config - 请求配置
   * @returns {Promise}
   */
  get(url, config = {}) {
    return request({ ...config, method: "get", url });
  },

  /**
   * POST请求快捷方法
   * @param {String} url - 请求地址
   * @param {Object} data - 请求体数据
   * @param {Object} config - 请求配置
   * @returns {Promise}
   */
  post(url, data, config = {}) {
    return request({ ...config, method: "post", url, data });
  },

  /**
   * PUT请求快捷方法
   * @param {String} url - 请求地址
   * @param {Object} data - 请求体数据
   * @param {Object} config - 请求配置
   * @returns {Promise}
   */
  put(url, data, config = {}) {
    return request({ ...config, method: "put", url, data });
  },

  /**
   * DELETE请求快捷方法
   * @param {String} url - 请求地址
   * @param {Object} config - 请求配置
   * @returns {Promise}
   */
  delete(url, config = {}) {
    return request({ ...config, method: "delete", url });
  },

  /**
   * 取消所有未完成的请求
   */
  cancelAllRequests() {
    // 遍历所有控制器并取消请求
    abortControllerMap.forEach((controller) => {
      controller.abort("已取消所有请求");
    });
    // 清空所有映射表
    abortControllerMap.clear();
    manualKeyMap.clear();
  },

  /**
   * 通过手动标识取消指定请求
   * 别名：cancelRequestByKey
   */
  cancelRequest: cancelRequestByKey,

  /**
   * 暴露原始axios实例
   * 用于特殊场景下的原生调用
   */
  original: service,
};
